//
//  ALAsset+Downscaling.m
//  OntheRoadV4
//
//  Created by hz on 5/25/14.
//  Copyright (c) 2014 EricHuang. All rights reserved.
//

// For details, see http://mindsea.com/2012/12/18/downscaling-huge-alassets-without-fear-of-sigkill

#import "ALAsset+Downscaling.h"
#import <ImageIO/ImageIO.h>

@implementation ALAsset (Downscaling)

-(CGSize)dimensions
{
    CGSize imgSize = [self.defaultRepresentation dimensions];
    return imgSize;
}

-(BOOL)hasOriginalFile
{
    return YES;
}

-(UIImage*)minThumbnail
{
    return [UIImage imageWithCGImage:self.thumbnail];
}

-(id)assetURL
{
    return [self valueForProperty:ALAssetPropertyAssetURL];
}

- (NSInteger)maxShareImageSizeForOTR
{
    
    CGSize imgSize = [[self defaultRepresentation] dimensions];
    if (imgSize.height * imgSize.width <= 4000000) {
        return MAX(imgSize.width, imgSize.height);
    } else {
        CGFloat scale = imgSize.height * imgSize.width / 4000000;
        scale = sqrt(scale);
        
        NSInteger width = roundf(imgSize.width / scale);
        width = ((width % 2 == 0) ? width : width + 1);
        
        NSInteger height = roundf(imgSize.height / scale);
        height = ((height % 2 == 0) ? height : height + 1);

        return MAX(width, height);
    }
}

- (NSInteger)screenThumbnailSizeForOTR
{
    CGSize imgSize = [[self defaultRepresentation] dimensions];
    CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
    CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
    CGFloat screenScale = [UIScreen mainScreen].scale;

    if (imgSize.height > imgSize.width * screenHeight / screenWidth) {
        return (screenHeight - 100) * screenScale;
    } else {
        return screenWidth * screenScale;
    }
}

- (NSInteger)screenMinSizeForOTR
{
    //CGSize imgSize = [[self defaultRepresentation] dimensions];
    CGFloat screenWidth = 67;
   // CGFloat screenHeight = 67;
    CGFloat screenScale = [UIScreen mainScreen].scale;
    
//    if (imgSize.height > imgSize.width * screenHeight / screenWidth) {
//        return (screenHeight - 100) * screenScale;
//    } else {
        return screenWidth * screenScale;
//}
}


// Helper methods for thumbnailForAsset:maxPixelSize:
static size_t getAssetBytesCallback(void *info, void *buffer, off_t position, size_t count) {
    ALAssetRepresentation *rep = (__bridge id)info;
    
    NSError *error = nil;
    size_t countRead = [rep getBytes:(uint8_t *)buffer fromOffset:position length:count error:&error];
    
    if (countRead == 0 && error) {
        // We have no way of passing this info back to the caller, so we log it, at least.
        NSLog(@"thumbnailForAsset:maxPixelSize: got an error reading an asset: %@", error);
    }
    
    return countRead;
}

static void releaseAssetCallback(void *info) {
    // The info here is an ALAssetRepresentation which we CFRetain in thumbnailForAsset:maxPixelSize:.
    // This release balances that retain.
    CFRelease(info);
}

// Returns a UIImage for the given asset, with size length at most the passed size.
// The resulting UIImage will be already rotated to UIImageOrientationUp, so its CGImageRef
// can be used directly without additional rotation handling.
// This is done synchronously, so you should call this method on a background queue/thread.
- (UIImage *)thumbnailWithMaxPixelSize:(NSUInteger)size orientation:(UIImageOrientation)orientation
{
    NSParameterAssert(self != nil);
    NSParameterAssert(size > 0);
    
    ALAssetRepresentation *rep = [self defaultRepresentation];
    
    CGDataProviderDirectCallbacks callbacks = {
        .version = 0,
        .getBytePointer = NULL,
        .releaseBytePointer = NULL,
        .getBytesAtPosition = getAssetBytesCallback,
        .releaseInfo = releaseAssetCallback,
    };

    CGDataProviderRef provider = CGDataProviderCreateDirect((void *)CFBridgingRetain(rep), [rep size], &callbacks);
    CGImageSourceRef source = CGImageSourceCreateWithDataProvider(provider, NULL);

    CFDictionaryRef options;
    if (size > 0) {
        options = (__bridge CFDictionaryRef) @{
                                               (NSString *)kCGImageSourceCreateThumbnailFromImageAlways : @YES,
                                               (NSString *)kCGImageSourceThumbnailMaxPixelSize : [NSNumber numberWithInteger:size],
                                               (NSString *)kCGImageSourceCreateThumbnailWithTransform : @YES,
                                               };
    } else {
        options = (__bridge CFDictionaryRef) @{
                                               (NSString *)kCGImageSourceCreateThumbnailFromImageAlways : @YES,
                                               (NSString *)kCGImageSourceCreateThumbnailWithTransform : @YES,
                                               };
    }

    CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(source, 0, options);

    CFRelease(source);
    CFRelease(provider);

    if (!imageRef) {
        return nil;
    }

    UIImage *toReturn = [UIImage imageWithCGImage:imageRef scale:1.0f orientation:orientation];
    
    CFRelease(imageRef);
    
    return toReturn;
}

- (NSData *)thumbnaiDataWithMaxPixelSize:(NSUInteger)size
{
    NSParameterAssert(self != nil);
//    NSParameterAssert(size > 0);
    
    ALAssetRepresentation *rep = [self defaultRepresentation];
    
    CGDataProviderDirectCallbacks callbacks = {
        .version = 0,
        .getBytePointer = NULL,
        .releaseBytePointer = NULL,
        .getBytesAtPosition = getAssetBytesCallback,
        .releaseInfo = releaseAssetCallback,
    };
    
    CGDataProviderRef provider = CGDataProviderCreateDirect((void *)CFBridgingRetain(rep), [rep size], &callbacks);
    CGImageSourceRef source = CGImageSourceCreateWithDataProvider(provider, NULL);
    
    CFDictionaryRef options;
    if (size > 0) {
        options = (__bridge CFDictionaryRef) @{
                                               (NSString *)kCGImageSourceCreateThumbnailFromImageAlways : @YES,
                                               (NSString *)kCGImageSourceThumbnailMaxPixelSize : [NSNumber numberWithInteger:size],
                                               (NSString *)kCGImageSourceCreateThumbnailWithTransform : @YES,
                                               };
    } else {
        options = (__bridge CFDictionaryRef) @{
                                               (NSString *)kCGImageSourceCreateThumbnailFromImageAlways : @YES,
                                               (NSString *)kCGImageSourceCreateThumbnailWithTransform : @YES,
                                               };
    }

    CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(source, 0, options);
    
    CFRelease(source);
    CFRelease(provider);
    if (!imageRef) {
        return nil;
    }

    CFStringRef UTI = (__bridge CFStringRef)(rep.UTI);//CGImageSourceGetType(source); //this is the type of image (e.g., public.jpeg)

    //this will be the data CGImageDestinationRef will write into
    NSMutableData *dest_data = [NSMutableData data];

    CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)dest_data, UTI,  1, NULL);
    
    if(!destination) {
        NSLog(@"*** Fatal error: Could not create image destination ***");
        CFRelease(imageRef);
        return nil;
    }
    
    // 改变metaData中的方向信息，删除图片处理相关信息
    NSMutableDictionary *mutableMetaData = [NSMutableDictionary dictionaryWithDictionary:rep.metadata];
    [mutableMetaData setValue:nil forKey:@"AdjustmentXMP"];
    [mutableMetaData setObject:@(1) forKey:(NSString *)kCGImagePropertyOrientation];
    
    NSMutableDictionary *mutableTIFFDict = [rep.metadata valueForKey:(NSString *)kCGImagePropertyTIFFDictionary];
    if (mutableTIFFDict) {
        mutableTIFFDict = [mutableTIFFDict mutableCopy];
        [mutableTIFFDict setValue:@(1) forKey:(NSString *)kCGImagePropertyTIFFOrientation];
        [mutableMetaData setValue:mutableTIFFDict forKey:(NSString *)kCGImagePropertyTIFFDictionary];
    }

    //add the image contained in the image source to the destination, overidding the old metadata with our modified metadata
    CGImageDestinationAddImage(destination, imageRef, (__bridge CFDictionaryRef)mutableMetaData);
    if (! CGImageDestinationFinalize(destination)) {
        NSLog(@"*** Fatal error: Could not create data from image destination ***");
        CFRelease(destination);
        CFRelease(imageRef);
        return nil;
    }

    //cleanup
    CFRelease(destination);
    CFRelease(imageRef);

    return dest_data;
}

- (BOOL)writeAssetFullResolutionImageToFile:(NSString *)filePath
{
    NSParameterAssert(self != nil);
    //    NSParameterAssert(size > 0);

    @autoreleasepool {
        ALAssetRepresentation *rep = [self defaultRepresentation];
        
        CFStringRef UTI = (__bridge CFStringRef)(rep.UTI);//CGImageSourceGetType(source); //this is the type of image (e.g., public.jpeg)
        
        // this will be the file CGImageDestinationRef will write into
        NSURL *fileURL = [NSURL fileURLWithPath:filePath];
        CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)fileURL, UTI, 1, NULL);
        
        if(!destination) {
            NSLog(@"*** Fatal error: Could not create image destination ***");
            return NO;
        }
        
        BOOL imageIOSupportsFilter = !([[[UIDevice currentDevice] systemVersion] compare:@"8.0" options:NSNumericSearch] == NSOrderedAscending);
        BOOL assetAppliedFilter = NO;
        @autoreleasepool {
            assetAppliedFilter = [(NSString *)(rep.metadata[@"AdjustmentXMP"]) length] > 0;
        }
        
        CGImageSourceRef source = NULL;
        if (!imageIOSupportsFilter && assetAppliedFilter) {
            // 改变metaData中的方向信息，删除图片处理相关信息
            NSMutableDictionary *mutableMetaData = [NSMutableDictionary dictionaryWithDictionary:rep.metadata];
            [mutableMetaData setValue:nil forKey:@"AdjustmentXMP"];
            [mutableMetaData setObject:@(1) forKey:(NSString *)kCGImagePropertyOrientation];
            
            NSMutableDictionary *mutableTIFFDict = [rep.metadata valueForKey:(NSString *)kCGImagePropertyTIFFDictionary];
            if (mutableTIFFDict) {
                mutableTIFFDict = [mutableTIFFDict mutableCopy];
                [mutableTIFFDict setValue:@(1) forKey:(NSString *)kCGImagePropertyTIFFOrientation];
                [mutableMetaData setValue:mutableTIFFDict forKey:(NSString *)kCGImagePropertyTIFFDictionary];
            }
            
            CGImageDestinationAddImage(destination, [rep fullScreenImage], (__bridge CFDictionaryRef)mutableMetaData);
        } else {
            Byte *buffer = (Byte*)malloc(rep.size);
            NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:rep.size error:nil];
            NSData *data = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
            
            CFRelease(destination);
            return [data writeToFile:filePath atomically:YES];
        }
        
        if (! CGImageDestinationFinalize(destination)) {
            NSLog(@"*** Fatal error: Could not create data from image destination ***");
            if (source != NULL) CFRelease(source);
            
            CFRelease(destination);
            return NO;
        }

        //cleanup
        CFRelease(destination);
        if (source != NULL) CFRelease(source);
        return YES;
    }
}




- (BOOL)writeAssetThumbnailImageToFile:(NSString *)filePath maxSize:(NSInteger)maxSize
{
    NSParameterAssert(self != nil);
    //    NSParameterAssert(size > 0);

    @autoreleasepool {
        ALAssetRepresentation *rep = [self defaultRepresentation];
        
        BOOL imageIOSupportsFilter = !([[[UIDevice currentDevice] systemVersion] compare:@"8.0" options:NSNumericSearch] == NSOrderedAscending);
        BOOL assetAppliedFilter = NO;
        @autoreleasepool {
            assetAppliedFilter = [(NSString *)(rep.metadata[@"AdjustmentXMP"]) length] > 0;
        }
        
        NSMutableDictionary *mutableMetaData = [rep.metadata mutableCopy];
        CGImageRef imageRef = NULL;
        if (imageIOSupportsFilter || !assetAppliedFilter) {
            CGDataProviderDirectCallbacks callbacks = {
                .version = 0,
                .getBytePointer = NULL,
                .releaseBytePointer = NULL,
                .getBytesAtPosition = getAssetBytesCallback,
                .releaseInfo = releaseAssetCallback,
            };
            
            CGDataProviderRef provider = CGDataProviderCreateDirect((void *)CFBridgingRetain(rep), [rep size], &callbacks);
            CGImageSourceRef source = CGImageSourceCreateWithDataProvider(provider, NULL);
            
            CFDictionaryRef options;
            if (maxSize > 0) {
                options = (__bridge CFDictionaryRef) @{
                                                       (NSString *)kCGImageSourceCreateThumbnailFromImageAlways : @YES,
                                                       (NSString *)kCGImageSourceThumbnailMaxPixelSize : [NSNumber numberWithInteger:maxSize],
                                                       (NSString *)kCGImageSourceCreateThumbnailWithTransform : @YES,
                                                       };
            } else {
                options = (__bridge CFDictionaryRef) @{
                                                       (NSString *)kCGImageSourceCreateThumbnailFromImageAlways : @YES,
                                                       (NSString *)kCGImageSourceCreateThumbnailWithTransform : @YES,
                                                       };
            }
            
            imageRef = CGImageSourceCreateThumbnailAtIndex(source, 0, options);
            
            CFRelease(source);
            CFRelease(provider);
        } else {
            imageRef = CGImageRetain([rep fullScreenImage]);
        }
        
        if (!imageRef) {
            return NO;
        }
        
        // 改变metaData中的方向信息，删除图片处理相关信息
        [mutableMetaData setValue:nil forKey:@"AdjustmentXMP"];
        [mutableMetaData setObject:@(1) forKey:(NSString *)kCGImagePropertyOrientation];
        
        NSMutableDictionary *mutableTIFFDict = [rep.metadata valueForKey:(NSString *)kCGImagePropertyTIFFDictionary];
        if (mutableTIFFDict) {
            mutableTIFFDict = [mutableTIFFDict mutableCopy];
            [mutableTIFFDict setValue:@(1) forKey:(NSString *)kCGImagePropertyTIFFOrientation];
            [mutableMetaData setValue:mutableTIFFDict forKey:(NSString *)kCGImagePropertyTIFFDictionary];
        }
        
        CFStringRef UTI = (__bridge CFStringRef)(rep.UTI);//CGImageSourceGetType(source); //this is the type of image (e.g., public.jpeg)
        
        // this will be the file CGImageDestinationRef will write into
        NSURL *fileURL = [NSURL fileURLWithPath:filePath];
        CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)fileURL, UTI, 1, NULL);
        
        if(!destination) {
            NSLog(@"*** Fatal error: Could not create image destination ***");
            CFRelease(imageRef);
            return NO;
        }
        
        //add the image contained in the image source to the destination, overidding the old metadata with our modified metadata
        CGImageDestinationAddImage(destination, imageRef, (__bridge CFDictionaryRef)mutableMetaData);
        if (! CGImageDestinationFinalize(destination)) {
            NSLog(@"*** Fatal error: Could not create data from image destination ***");
            CFRelease(destination);
            CFRelease(imageRef);
            return NO;
        }
        
        //cleanup
        CFRelease(destination);
        CFRelease(imageRef);
        
        return YES;
    }
}



@end
